#include <QPainter>
#include <QTimer>
#include <QSoundEffect>
#include <QMouseEvent>
#include <QMessageBox>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QDebug>
#include <math.h>
#include <QUrl>
#include "mainwindow.h"

QSoundEffect placeStoneSound;
QSoundEffect defeatSound;
QSoundEffect victorySound;

const int BOARD_EDGE_SPACE = 30;
const int STONE_RADIUS = 15;
const int MOVE_MARKER_SIZE = 6;
const int GRID_SIZE = 40;
const int CLICK_TOLERANCE = 20;
const int AI_THINKING_DELAY = 700;

GomokuMainWindow::GomokuMainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    placeStoneSound.setSource(QUrl::fromLocalFile(":/res/sound/chessone.wav"));
    defeatSound.setSource(QUrl::fromLocalFile(":/res/sound/lose.wav"));
    victorySound.setSource(QUrl::fromLocalFile(":/res/sound/win.wav"));

    setFixedSize(BOARD_EDGE_SPACE * 2 + GRID_SIZE * BOARD_DIMENSION,
                 BOARD_EDGE_SPACE * 2 + GRID_SIZE * BOARD_DIMENSION);

    setMouseTracking(true);

    QMenu *gameMenu = menuBar()->addMenu(tr("Game"));
    QAction *actionPVP = new QAction("Person VS Person", this);
    connect(actionPVP, SIGNAL(triggered()), this, SLOT(startPvPGame()));
    gameMenu->addAction(actionPVP);

    QAction *actionPVE = new QAction("Person VS Computer", this);
    connect(actionPVE, SIGNAL(triggered()), this, SLOT(startPvEGame()));
    gameMenu->addAction(actionPVE);

    setupNewGame();
}

GomokuMainWindow::~GomokuMainWindow()
{
    if (gameEngine)
    {
        delete gameEngine;
        gameEngine = nullptr;
    }
}

void GomokuMainWindow::setupNewGame()
{
    gameEngine = new GomokuGameEngine;
    startPvPGame();
}

void GomokuMainWindow::startPvPGame()
{
    currentGameMode = HUMAN_VS_HUMAN;
    gameEngine->currentState = GAME_ACTIVE;
    gameEngine->initializeGame(currentGameMode);
    update();
}

void GomokuMainWindow::startPvEGame()
{
    currentGameMode = HUMAN_VS_AI;
    gameEngine->currentState = GAME_ACTIVE;
    gameEngine->initializeGame(currentGameMode);
    update();
}

void GomokuMainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);

    for (int i = 0; i < BOARD_DIMENSION + 1; i++)
    {
        painter.drawLine(BOARD_EDGE_SPACE + GRID_SIZE * i, BOARD_EDGE_SPACE,
                         BOARD_EDGE_SPACE + GRID_SIZE * i, size().height() - BOARD_EDGE_SPACE);
        painter.drawLine(BOARD_EDGE_SPACE, BOARD_EDGE_SPACE + GRID_SIZE * i,
                         size().width() - BOARD_EDGE_SPACE, BOARD_EDGE_SPACE + GRID_SIZE * i);
    }

    QBrush brush;
    brush.setStyle(Qt::SolidPattern);

    if (hoverRow > 0 && hoverRow < BOARD_DIMENSION &&
        hoverCol > 0 && hoverCol < BOARD_DIMENSION &&
        gameEngine->boardState[hoverRow][hoverCol] == 0)
    {
        if (gameEngine->isPlayerTurn)
            brush.setColor(Qt::white);
        else
            brush.setColor(Qt::black);
        painter.setBrush(brush);
        painter.drawRect(BOARD_EDGE_SPACE + GRID_SIZE * hoverCol - MOVE_MARKER_SIZE / 2,
                         BOARD_EDGE_SPACE + GRID_SIZE * hoverRow - MOVE_MARKER_SIZE / 2,
                         MOVE_MARKER_SIZE, MOVE_MARKER_SIZE);
    }

    for (int i = 0; i < BOARD_DIMENSION; i++)
        for (int j = 0; j < BOARD_DIMENSION; j++)
        {
            if (gameEngine->boardState[i][j] == 1)
            {
                brush.setColor(Qt::white);
                painter.setBrush(brush);
                painter.drawEllipse(BOARD_EDGE_SPACE + GRID_SIZE * j - STONE_RADIUS,
                                    BOARD_EDGE_SPACE + GRID_SIZE * i - STONE_RADIUS,
                                    STONE_RADIUS * 2, STONE_RADIUS * 2);
            }
            else if (gameEngine->boardState[i][j] == -1)
            {
                brush.setColor(Qt::black);
                painter.setBrush(brush);
                painter.drawEllipse(BOARD_EDGE_SPACE + GRID_SIZE * j - STONE_RADIUS,
                                    BOARD_EDGE_SPACE + GRID_SIZE * i - STONE_RADIUS,
                                    STONE_RADIUS * 2, STONE_RADIUS * 2);
            }
        }

    if (hoverRow > 0 && hoverRow < BOARD_DIMENSION &&
        hoverCol > 0 && hoverCol < BOARD_DIMENSION &&
        (gameEngine->boardState[hoverRow][hoverCol] == 1 ||
         gameEngine->boardState[hoverRow][hoverCol] == -1))
    {
        if (gameEngine->checkVictory(hoverRow, hoverCol) && gameEngine->currentState == GAME_ACTIVE)
        {
            gameEngine->currentState = GAME_WIN;
            victorySound.play();
            QString str;
            if (gameEngine->boardState[hoverRow][hoverCol] == 1)
                str = "white player";
            else if (gameEngine->boardState[hoverRow][hoverCol] == -1)
                str = "black player";
            QMessageBox::StandardButton btnValue = QMessageBox::information(this, "congratulations", str + " win!");

            if (btnValue == QMessageBox::Ok)
            {
                gameEngine->initializeGame(currentGameMode);
                gameEngine->currentState = GAME_ACTIVE;
            }
        }
    }

    if (gameEngine->checkDraw())
    {
        defeatSound.play();
        QMessageBox::StandardButton btnValue = QMessageBox::information(this, "oops", "dead game!");
        if (btnValue == QMessageBox::Ok)
        {
            gameEngine->initializeGame(currentGameMode);
            gameEngine->currentState = GAME_ACTIVE;
        }
    }
}

void GomokuMainWindow::mouseMoveEvent(QMouseEvent *event)
{
    int x = event->x();
    int y = event->y();

    if (x >= BOARD_EDGE_SPACE + GRID_SIZE / 2 &&
        x < size().width() - BOARD_EDGE_SPACE &&
        y >= BOARD_EDGE_SPACE + GRID_SIZE / 2 &&
        y < size().height()- BOARD_EDGE_SPACE)
    {
        int col = x / GRID_SIZE;
        int row = y / GRID_SIZE;

        int leftTopPosX = BOARD_EDGE_SPACE + GRID_SIZE * col;
        int leftTopPosY = BOARD_EDGE_SPACE + GRID_SIZE * row;

        hoverRow = -1;
        hoverCol = -1;
        int len = 0;

        len = sqrt((x - leftTopPosX) * (x - leftTopPosX) + (y - leftTopPosY) * (y - leftTopPosY));
        if (len < CLICK_TOLERANCE)
        {
            hoverRow = row;
            hoverCol = col;
        }
        len = sqrt((x - leftTopPosX - GRID_SIZE) * (x - leftTopPosX - GRID_SIZE) + (y - leftTopPosY) * (y - leftTopPosY));
        if (len < CLICK_TOLERANCE)
        {
            hoverRow = row;
            hoverCol = col + 1;
        }
        len = sqrt((x - leftTopPosX) * (x - leftTopPosX) + (y - leftTopPosY - GRID_SIZE) * (y - leftTopPosY - GRID_SIZE));
        if (len < CLICK_TOLERANCE)
        {
            hoverRow = row + 1;
            hoverCol = col;
        }
        len = sqrt((x - leftTopPosX - GRID_SIZE) * (x - leftTopPosX - GRID_SIZE) + (y - leftTopPosY - GRID_SIZE) * (y - leftTopPosY - GRID_SIZE));
        if (len < CLICK_TOLERANCE)
        {
            hoverRow = row + 1;
            hoverCol = col + 1;
        }
    }

    update();
}

void GomokuMainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    if (!(currentGameMode == HUMAN_VS_AI && !gameEngine->isPlayerTurn))
    {
        processHumanMove();
        if (gameEngine->currentMode == HUMAN_VS_AI && !gameEngine->isPlayerTurn)
        {
            QTimer::singleShot(AI_THINKING_DELAY, this, SLOT(processAIMove()));
        }
    }
}

void GomokuMainWindow::processHumanMove()
{
    if (hoverRow != -1 && hoverCol != -1 && gameEngine->boardState[hoverRow][hoverCol] == 0)
    {
        gameEngine->humanMove(hoverRow, hoverCol);
        placeStoneSound.play();
        update();
    }
}

void GomokuMainWindow::processAIMove()
{
    gameEngine->aiMove(hoverRow, hoverCol);
    placeStoneSound.play();
    update();
}
